package org.colafries.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * date util
 * @author colafries
 */
@Slf4j
public class DateTimeUtil {

    // ---------------------- format parse ----------------------
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private static final ThreadLocal<Map<String, DateTimeFormatter>> dateFormatThreadLocal = new ThreadLocal<Map<String, DateTimeFormatter>>();
    private static DateTimeFormatter getDateFormat(String pattern) {
        if (pattern==null || pattern.trim().isEmpty()) {
            throw new IllegalArgumentException("pattern cannot be empty.");
        }

        Map<String, DateTimeFormatter> dateFormatMap = dateFormatThreadLocal.get();
        if(dateFormatMap!=null && dateFormatMap.containsKey(pattern)){
            return dateFormatMap.get(pattern);
        }

        synchronized (dateFormatThreadLocal) {
            if (dateFormatMap == null) {
                dateFormatMap = new HashMap<String, DateTimeFormatter>();
            }
            dateFormatMap.put(pattern, DateTimeFormatter.ofPattern(pattern));
            dateFormatThreadLocal.set(dateFormatMap);
        }

        return dateFormatMap.get(pattern);
    }

    /**
     * format datetime. like "yyyy-MM-dd"
     *
     * @param dateTime
     * @return
     * @throws ParseException
     */
    public static String formatDate(LocalDateTime dateTime) {
        return format(dateTime, DATE_FORMAT);
    }

    /**
     * format date. like "yyyy-MM-dd HH:mm:ss"
     *
     * @param dateTime
     * @return
     * @throws ParseException
     */
    public static String formatDateTime(LocalDateTime dateTime) {
        return format(dateTime, DATETIME_FORMAT);
    }

    /**
     * format date
     *
     * @param date
     * @param patten
     * @return
     * @throws ParseException
     */
    public static String format(LocalDateTime date, String patten) {
        return getDateFormat(patten).format(date);
//        return date.format(getDateFormat(patten));
    }

    /**
     * parse date string, like "yyyy-MM-dd HH:mm:s"
     *
     * @param dateString
     * @return
     * @throws ParseException
     */
    public static LocalDateTime parseDate(String dateString){
        return parse(dateString, DATE_FORMAT);
    }

    /**
     * parse datetime string, like "yyyy-MM-dd HH:mm:ss"
     *
     * @param dateString
     * @return
     * @throws ParseException
     */
    public static LocalDateTime parseDateTime(String dateString) {
        return parse(dateString, DATETIME_FORMAT);
    }

    /**
     * parse date
     *
     * @param dateString
     * @param pattern
     * @return
     * @throws ParseException
     */
    public static LocalDateTime parse(String dateString, String pattern) {
        try {
            return LocalDateTime.parse(dateString,getDateFormat(pattern));
        } catch (Exception e) {
            log.warn("parse date error, dateString = {}, pattern={}; errorMsg = {}", dateString, pattern, e.getMessage());
            return null;
        }
    }

    public static LocalDateTime fromMillis(long millis) {
        // 将毫秒数转换为 Instant 对象
        Instant instant = Instant.ofEpochMilli(millis);
        // 将 Instant 转换为 LocalDateTime 对象
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }
    public static long toEpochMilli(LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        return instant.toEpochMilli();
    }

    public static Date toDate(LocalDateTime localDateTime) {
        // 将 LocalDateTime 转换为 Instant
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();

        // 将 Instant 转换为 Date
        return Date.from(instant);
    }
    public static LocalDateTime toLocalDateTime(Date date) {
        // 将 Date 转换为 Instant
        Instant instant = date.toInstant();

        // 将 Instant 转换为 LocalDateTime
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    public static LocalDateTime toLocalDateTime(Timestamp timestamp){
        return timestamp.toLocalDateTime();
    }


    // ---------------------- add date ----------------------

    public static LocalDateTime addYears(final LocalDateTime dateTime, final int amount) {
        return add(dateTime, ChronoUnit.YEARS, amount);
    }

    public static LocalDateTime addMonths(final LocalDateTime dateTime, final int amount) {
        return add(dateTime, ChronoUnit.MONTHS, amount);
    }

    public static LocalDateTime addDays(final LocalDateTime dateTime, final int amount) {
        return add(dateTime, ChronoUnit.DAYS, amount);
    }

    public static LocalDateTime addHours(final LocalDateTime dateTime, final int amount) {
        return add(dateTime, ChronoUnit.HOURS, amount);
    }

    public static LocalDateTime addMinutes(final LocalDateTime dateTime, final int amount) {
        return add(dateTime, ChronoUnit.MINUTES, amount);
    }

    private static LocalDateTime add(final LocalDateTime dateTime, final ChronoUnit unit, final long amount) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.plus(amount,unit);
    }



}